﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Common
{
    /// <summary>
    /// 人民币
    /// </summary>
    [DebuggerDisplay("{Amount} {Unit}")]
    public struct CNY
    {
        /// <summary>
        /// 人民币
        /// </summary>
        /// <param name="amount"></param>
        /// <param name="unit"></param>
        public CNY(decimal amount, CnyUnit unit)
        {
            this.Unit = unit;
            this.Amount = amount;
        }
        /// <summary>
        /// 货币单位
        /// </summary>
        public CnyUnit Unit { get; private set; }
        /// <summary>
        /// 金额数值
        /// </summary>
        public decimal Amount { get; private set; }
        /// <summary>
        /// 转换到指定单位
        /// </summary>
        /// <param name="toUnit"></param>
        /// <returns></returns>
        public CNY ConvertTo(CnyUnit toUnit)
        {
            if (this.Unit == toUnit)
            {
                return this;
            }
            if (this.Unit == CnyUnit.分)
            {
                return new CNY { Unit = toUnit, Amount = this.Amount / 100m };
            }
            return new CNY { Unit = toUnit, Amount = this.Amount * 100m };
        }
        /// <summary>
        /// 获取哈希值
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return Unit.GetHashCode() ^ this.Amount.GetHashCode();
        }
        /// <summary>
        /// 对象比较
        /// </summary>
        /// <param name="obj"></param>
        /// <returns>如果obj为空，返回false；如果obj类型不是CNY，返回false；如果obj的金额与当前实例不相等，返回false</returns>
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (obj.GetType() != this.GetType())
            {
                return false;
            }
            CNY target = (CNY)obj;
            var to = this.ConvertTo(target.Unit);
            return to.Amount == target.Amount;
        }
        /// <summary>
        /// 转换字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Amount.ToString();
        }
        /// <summary>
        /// 转换为整数型，注意：若对象实际值包含小数会产生异常
        /// </summary>
        /// <returns></returns>
        public int ToInt()
        {
            if (this.Amount > int.MaxValue || this.Amount < int.MinValue)
            {
                throw new Exceptions.JaredsException("金额数值超过可转换的边界值");
            }
            if (this.Amount - decimal.Ceiling(this.Amount) != 0)
            {
                throw new Exceptions.JaredsException("金额数值包含小数，整数转换会损失精度");
            }
            return Convert.ToInt32(this.Amount);
        }
        /// <summary>
        /// 带单位返回
        /// </summary>
        /// <param name="format"></param>
        /// <param name="withUnit"></param>
        /// <returns></returns>
        public string ToString(string format, bool withUnit)
        {
            var val = string.Concat(this.Amount.ToString(format));
            if (withUnit)
            {
                val += " " + this.Unit.ToString();
            }
            return val;
        }
        /// <summary>
        /// 判等
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns></returns>
        public static bool operator ==(CNY c1, CNY c2)
        {
            return c1.Equals(c2);
        }
        /// <summary>
        /// 判不等
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns></returns>
        public static bool operator !=(CNY c1, CNY c2)
        {
            return !c1.Equals(c2);
        }
        /// <summary>
        /// 两个货币相加
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns>返回以c1单位的货币对象</returns>
        public static CNY operator +(CNY c1, CNY c2)
        {
            var tmp = c2.ConvertTo(c1.Unit);
            return new CNY(c1.Amount + tmp.Amount, c1.Unit);
        }
        /// <summary>
        /// 两个货币相减，注意c1 减去 c2的结果不允许小于零。
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns>返回以c1单位的货币对象</returns>
        public static CNY operator -(CNY c1, CNY c2)
        {
            var tmp = c2.ConvertTo(c1.Unit);
            if (c1.Amount < tmp.Amount)
            {
                throw new ApplicationException("两个货币相减的结果不允许小于零");
            }
            return new CNY(c1.Amount - tmp.Amount, c1.Unit);
        }
        /// <summary>
        /// 判断大小
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns></returns>
        public static bool operator >(CNY c1, CNY c2)
        {
            return c1.ConvertTo(c2.Unit).Amount > c2.Amount;
        }
        /// <summary>
        /// 判断大小
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns></returns>
        public static bool operator >=(CNY c1, CNY c2)
        {
            return c1.ConvertTo(c2.Unit).Amount >= c2.Amount;
        }
        /// <summary>
        /// 判断大小
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns></returns>
        public static bool operator <(CNY c1, CNY c2)
        {
            return c1.ConvertTo(c2.Unit).Amount < c2.Amount;
        }
        /// <summary>
        /// 判断大小
        /// </summary>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns></returns>
        public static bool operator <=(CNY c1, CNY c2)
        {
            return c1.ConvertTo(c2.Unit).Amount <= c2.Amount;
        }
        /// <summary>
        /// 代表零
        /// </summary>
        public readonly static CNY Zero = new CNY(0, CnyUnit.元);
        /// <summary>
        /// 获取指定单位类型的金额
        /// </summary>
        /// <param name="toUnit"></param>
        /// <returns></returns>
        public decimal GetAmount(CnyUnit toUnit)
        {
            return this.ConvertTo(toUnit).Amount;
        }
    }
    /// <summary>
    /// 人民币单位
    /// </summary>
    public enum CnyUnit
    {
        /// <summary>
        /// 分
        /// </summary>
        分 = 1,
        /// <summary>
        /// 元
        /// </summary>
        元 = 2
    }
}
